import json
import tkinter as tk
from tkinter import messagebox, simpledialog
from collections import defaultdict
import os

class JSONParser:
    def __init__(self, json_string):
        self.json_string = json_string
        
    def parse(self):
        return json.loads(self.json_string)

class Student:
    def __init__(self, username, full_name, age, school, interests, relations, followed_courses):
        self.username = username
        self.full_name = full_name
        self.age = age
        self.school = school
        self.interests = interests
        self.relations = relations
        self.followed_courses = followed_courses
        self.grades = {}
        
    def add_grade(self, course_id, grade):
        if course_id not in self.grades:
            self.grades[course_id] = []
        self.grades[course_id].append(grade)
        
    def get_average_grade(self, course_id=None):
        if course_id:
            if course_id in self.grades and self.grades[course_id]:
                return sum(self.grades[course_id]) / len(self.grades[course_id])
            return 0
        else:
            all_grades = []
            for grades in self.grades.values():
                all_grades.extend(grades)
            return sum(all_grades) / len(all_grades) if all_grades else 0
        
    def __repr__(self):
        return f"{self.full_name} - {self.school} (Score: {self.get_average_grade():.2f})"

class Course:
    def __init__(self, course_id, title, department, credits, professor, schedule, description, prerequisites):
        self.course_id = course_id
        self.title = title
        self.department = department
        self.credits = credits
        self.professor = professor
        self.schedule = schedule
        self.description = description
        self.prerequisites = prerequisites
        
    def __repr__(self):
        return f"Course({self.course_id}: {self.title}, Prerequisites: {self.prerequisites})"

class Scheduler:
    def __init__(self, university):
        self.university = university
        self.course_schedule = {}
        self.requests = []
        
    def initialize_schedule(self):
        for course in self.university.courses:
            self.course_schedule[course.course_id] = course.schedule
            
    def add_request(self, course_id, new_time, priority):
        self.requests.append((course_id, new_time, priority))
        
    def process_requests(self):
        self.requests.sort(key=lambda x: x[2], reverse=True)
        for course_id, new_time, _ in self.requests:
            self.course_schedule[course_id] = new_time
        self.requests = []

class App:
    def __init__(self, root):
        self.root = root
        self.root.title("Course Management System")
        
        # Initialize data structures
        self.students = []
        self.courses = []
        self.scheduler = Scheduler(self)
        self.scheduler.initialize_schedule()
        
        # Create main frame
        main_frame = tk.Frame(root)
        main_frame.pack(padx=10, pady=10)
        
        # Create student list
        self.student_listbox = tk.Listbox(main_frame, width=50, height=20)
        self.student_listbox.pack(side=tk.LEFT, padx=5)
        
        # Create button frame
        button_frame = tk.Frame(main_frame)
        button_frame.pack(side=tk.RIGHT, padx=5)
        
        # Create buttons
        tk.Button(button_frame, text="Load Data", command=self.load_data).pack(pady=5)
        tk.Button(button_frame, text="Sort by Name", command=self.sort_students_by_name).pack(pady=5)
        tk.Button(button_frame, text="Sort by Score", command=self.sort_students_by_score).pack(pady=5)
        tk.Button(button_frame, text="Sort by Course Score", command=lambda: self.sort_students_by_course_score(simpledialog.askstring("Course ID", "Enter course ID:"))).pack(pady=5)
        tk.Button(button_frame, text="Enroll Student", command=self.enroll_student).pack(pady=5)
        tk.Button(button_frame, text="Request Schedule Change", command=self.request_schedule_change).pack(pady=5)
        tk.Button(button_frame, text="Process Schedule Requests", command=self.process_schedule_requests).pack(pady=5)
        tk.Button(button_frame, text="Show Schedule", command=self.update_schedule_display).pack(pady=5)
        
    def load_data(self):
        try:
            # Get the directory where the script is located
            script_dir = os.path.dirname(os.path.abspath(__file__))
            base_path = os.path.join(script_dir, "resources")
            
            # Load students data
            students_file = os.path.join(base_path, "students.json")
            print(f"Trying to open {students_file}")
            with open(students_file, 'r', encoding='utf-8') as f:
                students_data = JSONParser(f.read()).parse()
                print("Successfully loaded students data")
                
            # Load courses data
            courses_file = os.path.join(base_path, "courses.json")
            print(f"Trying to open {courses_file}")
            with open(courses_file, 'r', encoding='utf-8') as f:
                courses_data = JSONParser(f.read()).parse()
                print("Successfully loaded courses data")
                
            # Load grades data
            grades_file = os.path.join(base_path, "grades.json")
            print(f"Trying to open {grades_file}")
            with open(grades_file, 'r', encoding='utf-8') as f:
                grades_data = JSONParser(f.read()).parse()
                print("Successfully loaded grades data")
                
            # Create courses
            print("Creating courses...")
            for course_data in courses_data['courses']:
                course = Course(
                    course_data['courseId'],
                    course_data['title'],
                    course_data['department'],
                    course_data['credits'],
                    course_data['professor'],
                    course_data['schedule'],
                    course_data['description'],
                    course_data['prerequisites']
                )
                self.courses.append(course)
            print(f"Created {len(self.courses)} courses")
                
            # Create students
            print("Creating students...")
            for student_data in students_data['students']:
                student = Student(
                    student_data['username'],
                    student_data['fullName'],
                    student_data['age'],
                    student_data['school'],
                    student_data['interests'],
                    student_data['relations'],
                    student_data['followed_courses']
                )
                self.students.append(student)
            print(f"Created {len(self.students)} students")
                
            # Add grades
            print("Adding grades...")
            for course_id, grades in grades_data['grades_per_course'].items():
                for username, course_grades in grades.items():
                    student = next((s for s in self.students if s.username == username), None)
                    if student:
                        for grade in course_grades:
                            student.add_grade(course_id, grade)
            print("Grades added successfully")
                            
            self.update_student_list()
            messagebox.showinfo("Success", "Data loaded successfully")
        except FileNotFoundError as e:
            messagebox.showerror("Error", f"File not found: {str(e)}")
        except json.JSONDecodeError as e:
            messagebox.showerror("Error", f"Invalid JSON format: {str(e)}")
        except KeyError as e:
            messagebox.showerror("Error", f"Missing key in JSON data: {str(e)}")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to load data: {str(e)}\nType: {type(e)}")
            
    def update_student_list(self):
        self.student_listbox.delete(0, tk.END)
        for student in self.students:
            self.student_listbox.insert(tk.END, str(student))
            courses_str = ", ".join(student.followed_courses)
            self.student_listbox.insert(tk.END, f"    Courses: {courses_str}")
            
    def sort_students_by_name(self):
        n = len(self.students)
        for i in range(n):
            for j in range(0, n-i-1):
                if self.students[j].full_name > self.students[j+1].full_name:
                    self.students[j], self.students[j+1] = self.students[j+1], self.students[j]
        self.update_student_list()
        
    def sort_students_by_score(self):
        n = len(self.students)
        for i in range(n):
            for j in range(0, n-i-1):
                if self.students[j].get_average_grade() < self.students[j+1].get_average_grade():
                    self.students[j], self.students[j+1] = self.students[j+1], self.students[j]
        self.update_student_list()
        
    def sort_students_by_course_score(self, course_id):
        n = len(self.students)
        for i in range(n):
            for j in range(0, n-i-1):
                if self.students[j].get_average_grade(course_id) < self.students[j+1].get_average_grade(course_id):
                    self.students[j], self.students[j+1] = self.students[j+1], self.students[j]
        self.update_student_list()
        
    def can_enroll(self, student, course_id):
        course = next((c for c in self.courses if c.course_id == course_id), None)
        if not course:
            return False, "Course does not exist"
            
        for prereq in course.prerequisites:
            if prereq not in student.followed_courses:
                return False, f"Prerequisite {prereq} not met"
                
        return True, "Can enroll"
        
    def enroll_student(self):
        student_name = simpledialog.askstring("Enroll Student", "Enter student name:")
        course_id = simpledialog.askstring("Enroll Student", "Enter course ID:")
        
        if student_name and course_id:
            student = next((s for s in self.students if s.full_name == student_name), None)
            if not student:
                messagebox.showinfo("Error", "Student not found")
                return
                
            can_enroll, message = self.can_enroll(student, course_id)
            if not can_enroll:
                messagebox.showinfo("Error", message)
                return
                
            if course_id not in student.followed_courses:
                student.followed_courses.append(course_id)
                messagebox.showinfo("Success", f"Successfully enrolled in {course_id}")
            else:
                messagebox.showinfo("Info", f"Already enrolled in {course_id}")
                
            self.update_student_list()
            
    def request_schedule_change(self):
        course_id = simpledialog.askstring("Schedule Change", "Enter course ID:")
        new_time = simpledialog.askstring("Schedule Change", "Enter new time:")
        priority = simpledialog.askinteger("Schedule Change", "Enter priority (1-10):", minvalue=1, maxvalue=10)
        
        if course_id and new_time and priority:
            self.scheduler.add_request(course_id, new_time, priority)
            messagebox.showinfo("Success", "Schedule change request submitted")

    def process_schedule_requests(self):
        self.scheduler.process_requests()
        self.update_student_list()
        
    def update_schedule_display(self):
        schedule_list = ""
        for course_id, time in self.scheduler.course_schedule.items():
            course = next((c for c in self.courses if c.course_id == course_id), None)
            if course:
                schedule_list += f"{course.title}: {time}\n"
        messagebox.showinfo("Current Schedule", schedule_list)

def main():
    root = tk.Tk()
    app = App(root)
    root.mainloop()

if __name__ == "__main__":
    main()
